#include <GL/freeglut.h>
#include <cstdlib>
#include <cmath>
using namespace std;

GLsizei winWidth = 600;
GLsizei winHeight = 600;

// 设置世界坐标系显示范围
GLfloat xwcMin = -300, xwcMax = 300;
GLfloat ywcMin = -300, ywcMax = 300;

// 定义三维点数据结构
struct wcPt3D
{
    GLfloat x, y, z;
};
// 设置显示窗口背景色为白色
void init()
{
    glClearColor(1, 1, 1, 0);
}
// 三维旋转变换，参数为旋转轴（由点P1和P2定义）和旋转角度（thetaDegrees）
void rotate3D(wcPt3D p1, wcPt3D p2, GLfloat thetaDegrees)
{
    // 计算旋转轴的矢量
    GLfloat vx = (p2.x - p1.x);
    GLfloat vy = (p2.y - p1.y);
    GLfloat vz = (p2.z - p1.z);

    // 通过平移-旋转-平移复合变换完成任意轴的旋转（注意OpenGL中的反序表示）
    glTranslatef(p1.x, p1.y, p1.z);      // ③移动p1到原始位置
    glRotatef(thetaDegrees, vx, vy, vz); // ②关于通过坐标原点的坐标轴旋转
    glTranslatef(-p1.x, -p1.y, -p1.z);   // ①移动p1到原点位置
}
// 三维比例放缩变换，参数为比例系数sx、sy、sz和固定点fixedPt
void scale3D(GLfloat sx, GLfloat sy, GLfloat sz, wcPt3D fixedPt)
{
    // 通过平移-缩放-平移复合复合变换完成任意点为中心点的比例缩放
    glTranslatef(fixedPt.x, fixedPt.y, fixedPt.z);    // ③反平移到原始位置
    glScalef(sx, sy, sz);                             // ②基于原点的比例缩放变换
    glTranslatef(-fixedPt.x, -fixedPt.y, -fixedPt.z); // ①移动固定点到坐标原点
}

void display()
{
    // 设置变换中心点位置
    wcPt3D centroidPt, R_p1, R_p2;

    centroidPt.x = 50;
    centroidPt.y = 100;
    centroidPt.z = 0;

    R_p1 = centroidPt;
    R_p2.x = 50;
    R_p2.y = 100;
    R_p2.z = 1;

    // 设置几何变换参数
    wcPt3D p1, p2, fixedPt;
    p1 = R_p1;
    p2 = R_p2;
    fixedPt = centroidPt;

    GLfloat tx = 0, ty = 100, tz = 0;
    GLfloat sx = 0.5, sy = 0.5, sz = 1;
    GLfloat thetaDegrees = 90;

    glClear(GL_COLOR_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    // 绘制变换前几何对象
    glColor3f(0, 0, 1);
    glRecti(50, 100, 200, 150);

    // 执行几何变换
    glTranslatef(tx, ty, tz);       // ③平移变换
    scale3D(sx, sy, sz, fixedPt);   // ②比例缩放变换
    rotate3D(p1, p2, thetaDegrees); // ①旋转变换

    // 绘制变换后几何对象
    glColor3f(1, 0, 0);
    glRecti(50, 100, 200, 150);

    glFlush();
}

void reshape(GLint width, GLint height)
{
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(xwcMin, xwcMax, ywcMin, ywcMax);
    glClear(GL_COLOR_BUFFER_BIT);
}

void main(int argc, char **argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize(winWidth, winHeight);
    glutCreateWindow("三维几何变换实例-OpenGL版复合变换");

    init();
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);

    glutMainLoop();
}
